package com.sc.nft.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.net.URLDecoder;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.sc.nft.config.hna.HnaConfig;
import com.sc.nft.config.lianlian.LlConfig;
import com.sc.nft.config.sand.SandConfig;
import com.sc.nft.constant.HnaPayConstants;
import com.sc.nft.dao.*;
import com.sc.nft.dto.*;
import com.sc.nft.dto.hna.HnaFastPayReq;
import com.sc.nft.entity.*;
import com.sc.nft.entity.dto.SaasTaskBurialPointDTO;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.helper.MessageQueueHelper;
import com.sc.nft.hna.form.*;
import com.sc.nft.service.*;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.util.BankCardUtil;
import com.sc.nft.util.OrderNoUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@RequiredArgsConstructor
public class UserHnaExtServiceImpl implements UserHnaExtService {

  private static final String SUCCESS_RESULT_CODE = "0000";
  private static final String PROCESS_RESULT_CODE = "9999";

  private final UserHnaExtDao userHnaExtDao;

  private final HnaConfig hnaConfig;

  private final RedissonClient redissonClient;

  private final OrderDao orderDao;

  private final SecondOrderDao secondOrderDao;

  private final MallOrderService mallOrderService;

  private final MallPayOrderRecordService mallPayOrderRecordService;

  private final RoughStoneOrderDao roughStoneOrderDao;

  private final IdcardInfoDao idcardInfoDao;

  private final UserBankCardDao userBankCardDao;

  @Autowired
  PayOrderService payOrderService;

  private final PayOrderDao payOrderDao;

  private final MallPayOrderService mallPayOrderService;

  private final MallPayOrderRecordDao mallPayOrderRecordDao;

  private final WithdrawBankCardDao withdrawBankCardDao;

  private final BankBindApplyDao bankBindApplyDao;

  private final StaticDataDao staticDataDao;

  private final SandWithdrawRecordDao sandWithdrawRecordDao;
  private final MessageQueueHelper messageQueueHelper;

  @Override
  public String createAccount(UserInfo userInfo, UserHnaAccountCreateDTO dto) {
    RLock rLock = redissonClient.getLock("HNA_CREATE_ACCOUNT_" + userInfo.getId());
    try {
      rLock.tryLock(3L, 8L, TimeUnit.SECONDS);
      UserHnaExt userHnaExt = userHnaExtDao.getByUserId(userInfo.getId());
      IdcardInfo idcardInfo = idcardInfoDao.getByUserIdAndIsPass(userInfo.getId());
      Assert.notNull(idcardInfo, () -> new GlobalRunTimeException(ErrorCode.USER_NOT_SM));
      dto.setCertNo(idcardInfo.getCardId());
      dto.setRealName(idcardInfo.getName());
      if (null == userHnaExt) {
        userHnaExt = new UserHnaExt();
        userHnaExt.setUserId(userInfo.getId());
        userHnaExt.setStatus(AccountStatusEnum.OPENING);
        userHnaExtDao.save(userHnaExt);
      } else {
        if (Objects.equals(AccountStatusEnum.SUCCESS, userHnaExt.getStatus())) {
          throw new GlobalRunTimeException(ErrorCode.OPEN_ACCOUNT_EXISTS);
        }
      }
      Map<String, Object> param = R010TransForm.generateParam(userInfo.getId(), hnaConfig, dto);
      JSONObject jsonObject =
          this.postHna(userInfo.getId(), param, R010TransForm.MAIN_URL, R010TransForm.TRAN_CODE);
      String userId = jsonObject.getString("userId");
      if (StringUtils.isBlank(userId)) {
        log.error("新生支付开户接口返回参数的用户编号为空");
        throw new GlobalRunTimeException(ErrorCode.SYS_ERROR);
      }
      userHnaExt.setUserNo(userId);
      userHnaExt.setStatus(AccountStatusEnum.SUCCESS);
      userHnaExtDao.updateById(userHnaExt);
      SaasTaskBurialPointDTO saasTaskBurialPointDTO=new SaasTaskBurialPointDTO();
      messageQueueHelper.sendMessage(MessageTopic.DISK_TAKER,saasTaskBurialPointDTO.wallet(userInfo.getId(),SaasTaskTypeEnum.OPEN_WALLET_FOR_THE_FIRST_TIME,BigDecimal.ONE,1,null),SaasTaskTypeEnum.OPEN_WALLET_FOR_THE_FIRST_TIME.name());
      messageQueueHelper.sendMessage(MessageTopic.DISK_TAKER,saasTaskBurialPointDTO.wallet(userInfo.getId(),SaasTaskTypeEnum.FIRST_TIME_OPEN_WALLET_INVITATION,BigDecimal.ONE,1,null),SaasTaskTypeEnum.FIRST_TIME_OPEN_WALLET_INVITATION.name());
      return "success";
    } catch (Exception e) {
      if (e instanceof GlobalRunTimeException) {
        throw (GlobalRunTimeException) e;
      } else {
        log.error("加锁失败，", e);
        throw new GlobalRunTimeException(ErrorCode.SYS_ERROR);
      }
    } finally {
      if (rLock.isLocked() && rLock.isHeldByCurrentThread()) {
        rLock.unlock();
      }
    }
  }

  @Override
  public JSONObject fastPay(UserInfo userInfo, HnaFastPayDTO dto) {
   return
        this.postHna(
            userInfo.getId(),
            T007TransForm.generateParam(hnaConfig, dto),
            T007TransForm.MAIN_URL,
            T007TransForm.TRAN_CODE);

  }

  @Override
  public String mallFastPay(UserInfo userInfo, HnaFastPayReq req) {
    long orderId = req.getOrderIds().get(0);
    MallOrder order = mallOrderService.getById(orderId);
    IdcardInfo idcardInfo = this.checkPayOrder(order, userInfo);
    UserBankCard userBankCard = userBankCardDao.getById(req.getCardId());
    Assert.isTrue(
        ObjectUtil.isNotNull(userBankCard) || userBankCard.getChannel() != PayChannelEnum.HNA_PAY,
        () -> new GlobalRunTimeException(ErrorCode.CARD_NOT_FOUND));
    Assert.isTrue(
        userBankCard.getUserId().longValue() == userInfo.getId().longValue(),
        () -> new GlobalRunTimeException(ErrorCode.CARD_NOT_FOUND));

    Assert.isNull(
        mallPayOrderRecordService.getPaySuccessByOrderNo(order.getOrderNo()),
        () -> new GlobalRunTimeException(ErrorCode.ORDER_PAID));
    MallPayOrderRecord payOrder = new MallPayOrderRecord();
    DateTime now = DateTime.now();
    payOrder.setPayType(PayMethodEnum.HNA_PAY);
    payOrder.setReqTime(now);
    payOrder.setCloseTime(order.getExpiredTime());
    payOrder.setOrderNo(order.getOrderNo());
    payOrder.setReqIp(req.getIp());
    payOrder.setOrderType(PayTypeEnum.PAY);
    payOrder.setPayAmount(order.getPayPrice());
    payOrder.setUserCardId(req.getCardId());
    payOrder.setRefundAmount(BigDecimal.ZERO);
    payOrder.setChannel(payOrder.getPayType().getChannel());
    payOrder.setTradeNo(UUID.fastUUID().toString().replaceAll("-", ""));
    payOrder.setStatus(PayStatusEnum.INIT);
    payOrder.setNotifyUrl(LlConfig.mallLlNotifyUrl);
    payOrder.setFrontUrl(SandConfig.FAST_RETURN_URL);
    payOrder.setUserId(order.getUserId());
    HnaFastPayDTO dto = new HnaFastPayDTO();
    dto.setPayType("2");
    dto.setCardNo(userBankCard.getBankCardNo());
    dto.setIdentityCode(idcardInfo.getCardId());
    dto.setHolderName(idcardInfo.getName());
    dto.setIdentityType("1");
    dto.setMobileNo(userBankCard.getMobile());
    dto.setReceiveUserId(hnaConfig.getMerId());
    dto.setPayOrderNo(payOrder.getTradeNo());
    dto.setTranAmount(payOrder.getPayAmount().toPlainString());
    JSONObject jsonObject =
        this.postHna(
            userInfo.getId(),
            T007TransForm.generateParam(hnaConfig, dto),
            T007TransForm.MAIN_URL,
            T007TransForm.TRAN_CODE);
    payOrder.setReqResult(jsonObject.toJSONString());
    payOrder.setOutTradeNo(jsonObject.getString("ncountOrderId"));
    payOrder.insert();
    return payOrder.getOrderNo();
  }

  private PayOrder buildPayOrder(UserInfo userInfo, HnaFastPayReq req, HnaFastPayDTO dto) {
    IdcardInfo idcardInfo = idcardInfoDao.getByUserIdAndIsPass(userInfo.getId());
    Assert.notNull(idcardInfo, () -> new GlobalRunTimeException(ErrorCode.USER_NOT_SM));
    UserBankCard userBankCard = userBankCardDao.getById(req.getCardId());
    Assert.isTrue(
        ObjectUtil.isNotNull(userBankCard) || userBankCard.getChannel() != PayChannelEnum.HNA_PAY,
        () -> new GlobalRunTimeException(ErrorCode.CARD_NOT_FOUND));
    Assert.isTrue(
        userBankCard.getUserId().longValue() == userInfo.getId().longValue(),
        () -> new GlobalRunTimeException(ErrorCode.CARD_NOT_FOUND));
    dto.setPayType("2");
    dto.setCardNo(userBankCard.getBankCardNo());
    dto.setIdentityCode(idcardInfo.getCardId());
    dto.setHolderName(idcardInfo.getName());
    dto.setIdentityType("1");
    dto.setMobileNo(userBankCard.getMobile());
    dto.setReceiveUserId(hnaConfig.getMerId());
    PayOrder payOrder = new PayOrder();
    DateTime now = DateTime.now();
    payOrder.setPayType(PayMethodEnum.HNA_PAY);
    payOrder.setReqTime(now);
    payOrder.setReqIp(req.getIp());
    payOrder.setUserId(userInfo.getId());
    payOrder.setOrderType(PayTypeEnum.PAY);
    payOrder.setUserCardId(req.getCardId());
    payOrder.setRefundAmount(BigDecimal.ZERO);
    payOrder.setChannel(payOrder.getPayType().getChannel());
    payOrder.setTradeNo(UUID.fastUUID().toString().replaceAll("-", ""));
    dto.setPayOrderNo(payOrder.getTradeNo());
    payOrder.setStatus(PayStatusEnum.INIT);
    payOrder.setNotifyUrl(hnaConfig.getT007NotifyUrl());
    payOrder.setFrontUrl(
        LlConfig.paySuccessReturnUrl + "?jump=page&type=1&orderNo=" + payOrder.getOrderNo());
    int size = req.getOrderIds().size();
    long orderId = req.getOrderIds().get(0);
    if (Boolean.TRUE.equals(req.getStone())) {
      RoughStoneOrder order = roughStoneOrderDao.getById(orderId);
      this.dealWithStoneOrder(userInfo, order, payOrder, dto);
    } else {
      if (size == 1) {
        if (Boolean.TRUE.equals(req.getSecond())) {
          this.dealWithSecondOrder(userInfo, req.getOrderIds(), payOrder, dto);
        } else {
          this.dealWithOrder(userInfo, orderId, payOrder, dto);
        }
      } else {
        this.dealWithSecondOrder(userInfo, req.getOrderIds(), payOrder, dto);
      }
    }
    return payOrder;
  }

  private void dealWithOrder(
      UserInfo userInfo, long orderId, PayOrder payOrder, HnaFastPayDTO dto) {
    Order order = orderDao.getById(orderId);
    Assert.notNull(order, () -> new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND));
    Assert.isTrue(
        order.getUserId().longValue() == userInfo.getId().longValue(),
        () -> new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND));
    Assert.isTrue(
        order.getStatus() == OrderStatusEnum.WAIT_PAY,
        () -> new GlobalRunTimeException(ErrorCode.ORDER_STATUS_ERROR));
    payOrder.setCloseTime(order.getCloseTime());
    payOrder.setOrderNo(order.getOrderNo());
    payOrder.setPayAmount(order.getProductPrice());
    payOrder.setUserId(order.getUserId());
    dto.setTranAmount(payOrder.getPayAmount().toPlainString());
    payOrder.insert();
  }

  private void dealWithStoneOrder(
      UserInfo userInfo, RoughStoneOrder order, PayOrder payOrder, HnaFastPayDTO dto) {
    Assert.notNull(order, () -> new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND));
    Assert.isTrue(
        order.getUserId().longValue() == userInfo.getId().longValue(),
        () -> new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND));
    Assert.isTrue(
        order.getStatus() == OrderStatusEnum.WAIT_PAY,
        () -> new GlobalRunTimeException(ErrorCode.ORDER_STATUS_ERROR));
    payOrder.setCloseTime(order.getCloseTime());
    payOrder.setOrderNo(order.getOrderNo());
    payOrder.setPayAmount(order.getProductPrice());
    payOrder.setUserId(order.getUserId());
    payOrder.setOrderType(PayTypeEnum.PAY_SALVAGE);
    dto.setTranAmount(payOrder.getPayAmount().toPlainString());
    payOrder.insert();
  }

  private void dealWithSecondOrder(
      UserInfo userInfo, List<Long> orderIds, PayOrder payOrder, HnaFastPayDTO dto) {
    List<SecondOrder> secondOrders = secondOrderDao.listByIds(orderIds);
    Assert.notEmpty(secondOrders, () -> new GlobalRunTimeException("订单不存在"));
    Assert.isTrue(
        secondOrders.size() == orderIds.size(), () -> new GlobalRunTimeException("支付订单数量不一致"));
    Assert.notEmpty(secondOrders, () -> new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND));
    Date firstTime = new Date();
    BigDecimal sumAmount = BigDecimal.ZERO;
    for (SecondOrder secondOrder : secondOrders) {
      Assert.isTrue(
          secondOrder.getBuyUserId().longValue() == userInfo.getId().longValue(),
          () -> new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND));
      Assert.isTrue(
          secondOrder.getStatus() == OrderStatusEnum.WAIT_PAY,
          () -> new GlobalRunTimeException(ErrorCode.ORDER_STATUS_ERROR));
      if (firstTime.after(secondOrder.getCreateTime())) {
        firstTime = secondOrder.getCreateTime();
      }
      sumAmount = sumAmount.add(secondOrder.getBuyPrice());
    }
    payOrder.setCloseTime((DateUtil.offsetMinute(firstTime, 3)));
    payOrder.setOrderNo(secondOrders.get(0).getOrderNo());
    payOrder.setPayAmount(sumAmount);
    payOrder.setOrderType(PayTypeEnum.SECOND);
    dto.setTranAmount(payOrder.getPayAmount().toPlainString());
    payOrder.insert();
    for (SecondOrder secondOrder : secondOrders) {
      PayOrderItem payOrderItem = new PayOrderItem();
      payOrderItem.setPayOrderId(payOrder.getId());
      payOrderItem.setOrderId(secondOrder.getId());
      payOrderItem.setAmount(secondOrder.getBuyPrice());
      payOrderItem.insert();
    }
  }



  @Override
  public String fastPayConfirm(UserInfo userInfo, HnaFastPayConfirmDTO dto) {
    String outTradeNo = this.checkOrder(userInfo.getId(), dto.getNcountOrderId());
    if (StringUtils.isBlank(dto.getPaymentTerminalInfo())) {
      dto.setPaymentTerminalInfo(HnaPayConstants.PAYMENT_TERMINAL_INFO);
    }
    if (StringUtils.isBlank(dto.getReceiverTerminalInfo())) {
      dto.setReceiverTerminalInfo(HnaPayConstants.RECEIVER_TERMINAL_INFO);
    }
    if (StringUtils.isBlank(dto.getDeviceInfo())) {
      dto.setDeviceInfo(dto.getMerUserIp() + HnaPayConstants.DEVICE_INFO);
    }
    dto.setNcountOrderId(outTradeNo);
    JSONObject jsonObject =
            this.postHna(
                    userInfo.getId(),
                    T008TransForm.generateParam(hnaConfig, dto),
                    T008TransForm.MAIN_URL,
                    T008TransForm.TRAN_CODE);
    log.info("新生支付确认返回结果：{}", jsonObject.toJSONString());
    return outTradeNo;
  }

  @Override
  public void fastPayConfirm(PayOrder payOrder,String verifyCode) {
    Assert.notBlank(verifyCode,()->new GlobalRunTimeException("请填写验证码"));
    HnaFastPayConfirmDTO dto = new HnaFastPayConfirmDTO();
    dto.setPaymentTerminalInfo(HnaPayConstants.PAYMENT_TERMINAL_INFO);
    dto.setReceiverTerminalInfo(HnaPayConstants.RECEIVER_TERMINAL_INFO);
    dto.setDeviceInfo(dto.getMerUserIp() + HnaPayConstants.DEVICE_INFO);
    dto.setNcountOrderId(payOrder.getOutTradeNo());
    dto.setMerUserIp(payOrder.getReqIp());
    dto.setSmsCode(verifyCode);
    JSONObject jsonObject =
        this.postHna(
                payOrder.getUserId(),
            T008TransForm.generateParam(hnaConfig, dto),
            T008TransForm.MAIN_URL,
            T008TransForm.TRAN_CODE);
    log.info("新生支付确认返回结果：{}", jsonObject.toJSONString());
  }

  private String checkOrder(long userId, String orderNo) {
    String result;
    MallPayOrderRecord payOrder = mallPayOrderRecordDao.getPayByOrderNo(orderNo);
    if (null == payOrder) {
      PayOrder order = payOrderDao.getByOrderNo(orderNo);
      if (null == order) {
        throw new GlobalRunTimeException("订单号找不到");
      } else {
        result = order.getOutTradeNo();
        if (!Objects.equals(userId, order.getUserId())) {
          throw new GlobalRunTimeException("订单号找不到");
        }
        if (Objects.equals(order.getStatus(), PayStatusEnum.SUCCESS)) {
          throw new GlobalRunTimeException("订单已支付");
        }
        if (!Objects.equals(order.getPayType(), PayMethodEnum.HNA_PAY)) {
          throw new GlobalRunTimeException("找不到该订单");
        }
      }
    } else {
      result = payOrder.getOutTradeNo();
      if (!Objects.equals(userId, payOrder.getUserId())) {
        throw new GlobalRunTimeException("订单号找不到");
      }
      if (Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS)) {
        throw new GlobalRunTimeException("订单已支付");
      }
      if (!Objects.equals(payOrder.getPayType(), PayMethodEnum.HNA_PAY)) {
        throw new GlobalRunTimeException("找不到该订单");
      }
    }
    return result;
  }

  @Override
  public String fastPayNotify(Map<String, Object> param) {
    log.info("-------------进入新生支付快捷支付异步回调-------------");
    JSONObject jsonObject = (JSONObject) JSONObject.toJSON(param);
    this.checkNotifySign(
        jsonObject, BaseTransForm.getNotifyVerifyDataByTranCode("T008", jsonObject));
    String orderId = jsonObject.getString("merOrderId");
    if (StringUtils.isBlank(orderId)) {
      throw new GlobalRunTimeException("商户订单号为空");
    }
    String resultCode = jsonObject.getString("resultCode");
    if (!SUCCESS_RESULT_CODE.equals(resultCode)) {
      return "200";
    }
    RLock rLock = redissonClient.getLock("FAST_PAY_NOTIFY_" + orderId);
    try {
      rLock.tryLock(3L, 8L, TimeUnit.SECONDS);
      log.info("新生快捷支付回调业务处理");
      String payTime = jsonObject.getString("tranFinishTime");
      PayOrder order = payOrderDao.getByTradeNo(orderId);
      if (null != order) {
        payOrderService.orderPaySuccess(order, payTime, jsonObject.toJSONString());
      } else {
        mallPayOrderService.paySuccess(
            orderId, DateTime.of(payTime, "yyyyMMddHHmmss"), jsonObject.toJSONString(), PayMethodEnum.HNA_PAY);
      }
      return "200";
    } catch (Exception e) {
      if (e instanceof GlobalRunTimeException) {
        throw new GlobalRunTimeException(e.getMessage());
      } else {
        log.error("新生快捷支付异步回调处理出错，", e);
        throw new GlobalRunTimeException(ErrorCode.SYS_ERROR);
      }
    } finally {
      if (rLock.isLocked() && rLock.isHeldByCurrentThread()) {
        rLock.unlock();
      }
    }
  }

  @Override
  public String withdrawNotify(Map<String, Object> param) {
    JSONObject jsonObject = (JSONObject) JSONObject.toJSON(param);
    this.checkNotifySign(
        jsonObject, BaseTransForm.getNotifyVerifyDataByTranCode("T002", jsonObject));
    String merOrderId = jsonObject.getString("merOrderId");
    if (SUCCESS_RESULT_CODE.equals(jsonObject.getString("resultCode"))) {
      sandWithdrawRecordDao.success(merOrderId);
    } else {
      sandWithdrawRecordDao.fail(merOrderId,
              URLDecoder.decode(jsonObject.getString("errorMsg"), Charset.defaultCharset())
              );
    }
    return "200";
  }



  @Override
  public long bindCard(UserInfo userInfo, HnaCardBindDTO dto) {
    UserHnaExt userHnaExt = this.userHnaExtDao.getByUserId(userInfo.getId());
    if (null == userHnaExt
        || !AccountStatusEnum.SUCCESS.equals(userHnaExt.getStatus())
        || StringUtils.isBlank(userHnaExt.getUserNo())) {
      throw new GlobalRunTimeException("用户未开户");
    }
    IdcardInfo idcardInfo = idcardInfoDao.getByUserIdAndIsPass(userInfo.getId());
    dto.setHolderName(idcardInfo.getName());
    dto.setIdentityCode(idcardInfo.getCardId());
    dto.setUserId(userHnaExt.getUserNo());
    dto.setIdentityType("1");
    WithdrawBankCard userCard =
        withdrawBankCardDao.findUserCard(userInfo.getId(), dto.getCardNo(), WalletChannelEnum.HNA);
    if (ObjectUtil.isNotNull(userCard)) {
      throw new GlobalRunTimeException("该银行卡已经绑定过了");
    }
    JSONObject jsonObject =
        this.postHna(
            userInfo.getId(),
            R007TransForm.generateParam(userHnaExt.getUserNo(), hnaConfig, dto),
            R007TransForm.MAIN_URL,
            R007TransForm.TRAN_CODE);
    BankBindApply bankBindApply = new BankBindApply();
    bankBindApply.setUserId(userInfo.getId());
    bankBindApply.setStatus(BankBindApplyStatus.NOT_APPROVAL);
    bankBindApply.setApplyNo(OrderNoUtil.getOrderNo());
    bankBindApply.setType(BankBindApplyType.BIND);
    bankBindApply.setCardNo(dto.getCardNo());
    bankBindApply.setName(dto.getHolderName());
    bankBindApply.setChannel(PayChannelEnum.HNA_PAY);
    bankBindApply.setCardType(CardTypeEnum.WITHDRAW);
    bankBindApply.setPhoneNo(dto.getMobileNo());
    bankBindApply.setCertificateNo(dto.getIdentityCode());
    bankBindApply.setBankName(BankCardUtil.getBankNameByCardNo(dto.getCardNo()));
    String token = jsonObject.getString("ncountOrderId");
    bankBindApply.setSdMsgNo(token);
    bankBindApply.insert();
    return bankBindApply.getId();
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public String bindCardConfirm(UserInfo userInfo, HnaCardBindConfirmDTO dto) {
    BankBindApply apply = bankBindApplyDao.getById(dto.getApplyId());
    Assert.notNull(apply, () -> new GlobalRunTimeException("未找到绑卡申请记录"));
    dto.setNcountOrderId(apply.getSdMsgNo());
    WithdrawBankCard userCard =
        withdrawBankCardDao.findUserCard(
            userInfo.getId(), apply.getCardNo(), WalletChannelEnum.HNA);
    if (ObjectUtil.isNotNull(userCard)) {
      throw new GlobalRunTimeException("该银行卡已经绑定过了");
    }
    JSONObject jsonObject =
        this.postHna(
            userInfo.getId(),
            R008TransForm.generateParam(hnaConfig, dto),
            R008TransForm.MAIN_URL,
            R008TransForm.TRAN_CODE);
    String bindNo = jsonObject.getString("bindCardAgrNo");
    userCard = new WithdrawBankCard();
    userCard.setUserId(userInfo.getId());
    userCard.setBankName(BankCardUtil.getBankNameByCardNo(apply.getCardNo()));
    userCard.setBankCardNo(apply.getCardNo());
    userCard.setChannel(WalletChannelEnum.HNA);
    userCard.setRelatedCardNo(bindNo);
    userCard.setBindId(apply.getId());
    userCard.setName(apply.getName());
    userCard.setMobile(apply.getPhoneNo());
    userCard.setIdentityNo(apply.getCertificateNo());
    userCard.setStatus(true);
    userCard.insert();
    apply.setStatus(BankBindApplyStatus.SUCCESS);
    apply.updateById();
    return bindNo;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void withdraw(UserInfo userInfo, HnaWithdrawDto dto) {
    if (!NumberUtil.isNumber(dto.getTranAmount())) {
      throw new GlobalRunTimeException("转账金额需为数字");
    }
    BigDecimal amount = new BigDecimal(dto.getTranAmount());
    RLock rLock = redissonClient.getLock("HNA_WITHDRAW_" + userInfo.getId());
    try {
      if (!rLock.tryLock(2,8,TimeUnit.SECONDS)) {
        log.error("新生支付提现加锁失败");
        throw new GlobalRunTimeException("提交失败，请重试");
      }
      WithdrawBankCard card = withdrawBankCardDao.getById(dto.getCardId());
      if (null == card) {
        throw new GlobalRunTimeException("找不到该卡");
      }
      if (card.getChannel() != WalletChannelEnum.HNA) {
        throw new GlobalRunTimeException("找不到该卡");
      }
      JSONObject json = this.userInfo(userInfo);
      BigDecimal balance = new BigDecimal(json.getString("balAmount"));
      if (balance.compareTo(amount) < 0) {
        throw new GlobalRunTimeException(ErrorCode.BALANCE_NO_ENOUGH);
      }
      //余额减去提现中的
      BigDecimal withdrawing = sandWithdrawRecordDao.sumWithdrawing4Hna(userInfo.getId());
      if (balance.compareTo(withdrawing.add(amount)) < 0) {
        throw new GlobalRunTimeException(ErrorCode.BALANCE_NO_ENOUGH);
      }
      String minimumWithdrawalAmountString =
              staticDataDao.getByType("minimum_withdrawal_amount").getValue();
      if (new BigDecimal(minimumWithdrawalAmountString).compareTo(amount) > 0) {
        throw new GlobalRunTimeException("提现金额不能小于" + minimumWithdrawalAmountString + "元");
      }
      if (NumberUtil.isGreater(amount,new BigDecimal("50000"))) {
        throw new GlobalRunTimeException("提现金额必须小于50000元");
      }
      this.saveWithdrawRecord(userInfo.getId(), dto, amount, card.getRelatedCardNo());
    } catch (Exception e) {
      if (e instanceof GlobalRunTimeException) {
        throw new GlobalRunTimeException(e.getMessage());
      }
      log.error("新生支付提现加锁逻辑出现问题，", e);
    } finally {
      if (rLock.isLocked() && rLock.isHeldByCurrentThread()) {
        rLock.unlock();
      }
    }

  }

  private void saveWithdrawRecord(
      long userId, HnaWithdrawDto dto, BigDecimal amount, String cardNo) {
    dto.setBindCardAgrNo(cardNo);
    dto.setNotifyUrl(hnaConfig.getT002NotifyUrl());
    SandWithdrawRecord withdrawRecord = new SandWithdrawRecord();
    withdrawRecord.setUserId(userId);
    withdrawRecord.setCustomerOrderNo(OrderNoUtil.getOrderNo());
    withdrawRecord.setAmount(amount);
    String fee = staticDataDao.getByType("withdrawal_fee").getValue();
    String minimumWithdrawalFee = staticDataDao.getByType("minimum_withdrawal_fee").getValue();
    BigDecimal feeAmount = amount.multiply(new BigDecimal(fee));
    feeAmount =
        feeAmount.compareTo(Convert.toBigDecimal(minimumWithdrawalFee)) > 0
            ? feeAmount
            : Convert.toBigDecimal(minimumWithdrawalFee);
      withdrawRecord.setFeeAmount(feeAmount);
    withdrawRecord.setRealAmount(amount.subtract(withdrawRecord.getFeeAmount()));
    withdrawRecord.setChannel(WalletChannelEnum.HNA);
    withdrawRecord.setSettleTime(DateUtil.tomorrow());
    withdrawRecord.setConfirmStatus(false);
    withdrawRecord.setRelatedCardNo(cardNo);
    withdrawRecord.setStatus(0);
    dto.setFeeAmount(feeAmount.toString());
    dto.setTranAmount(amount.toString());
    withdrawRecord.insert();
  }

  @Override
  public void doWithdraw(SandWithdrawRecord record, boolean pass, HnaWithdrawDto dto, String remark) {
    if (!pass) {
      record.setConfirmStatus(true);
      record.setRemark(remark);
      record.setStatus(2);
      record.updateById();
      return;
    }
    if (StringUtils.isBlank(dto.getPaymentTerminalInfo())) {
      dto.setPaymentTerminalInfo(HnaPayConstants.PAYMENT_TERMINAL_INFO);
    }
    String userNo = this.needOpenAccount(record.getUserId());
    dto.setUserId(userNo);
    if (StringUtils.isBlank(dto.getNotifyUrl())) {
      dto.setNotifyUrl(hnaConfig.getT002NotifyUrl());
    }
    if (StringUtils.isBlank(dto.getDeviceInfo())) {
      dto.setDeviceInfo(dto.getMerUserIp() + HnaPayConstants.DEVICE_INFO);
    }
    JSONObject result =
        this.postHna(
            record.getUserId(),
            T002TransForm.generateParam(hnaConfig, dto, record.getCustomerOrderNo()),
            T002TransForm.MAIN_URL,
            T002TransForm.TRAN_CODE);
    record.setRemark(result.getString("errorMsg"));
    record.setConfirmStatus(true);
    record.updateById();
  }

  @Override
  public JSONObject userInfo(UserInfo userInfo) {
    String userNo = this.needOpenAccount(userInfo.getId());
    return this.postHna(
        userInfo.getId(),
        Q001TransForm.generateParam(hnaConfig, userNo),
        Q001TransForm.MAIN_URL,
        Q001TransForm.TRAN_CODE);
  }

  @Override
  public String transfer(UserInfo userInfo, String tranAmount) {
    String userNo = this.needOpenAccount(userInfo.getId());
    JSONObject jsonObject =
        this.postHna(
            userInfo.getId(),
            T003TransForm.generateParam(hnaConfig, userNo, tranAmount),
            T003TransForm.MAIN_URL,
            T003TransForm.TRAN_CODE);
    return jsonObject.toJSONString();
  }

  @Override
  public JSONObject refund(UserInfo userInfo, HnaRefundDto dto) {
    return this.postHna(
        userInfo.getId(),
        T005TransForm.generateParam(hnaConfig, dto),
        T005TransForm.MAIN_URL,
        T005TransForm.TRAN_CODE);
  }

  @Override
  public String refundNotify(Map<String, Object> param) {
    log.info("-------------进入新生支付非商城退款异步回调-------------");
    JSONObject jsonObject = (JSONObject) JSONObject.toJSON(param);
    log.info("-------------新生支付非商城退款异步回调入参：{}-------------", jsonObject.toJSONString());
    this.checkNotifySign(
        jsonObject, BaseTransForm.getNotifyVerifyDataByTranCode("T005", jsonObject));
    return jsonObject.toJSONString();
  }

  private JSONObject postHna(long userId, Map<String, Object> param, String url, String tranCode) {
    SandBizRecord sandBizRecord = new SandBizRecord();
    sandBizRecord.setUserId(userId);
    sandBizRecord.setBizMethod(tranCode);
    String result = HttpUtil.post(url, param);
    sandBizRecord.setResult(result);
    sandBizRecord.setCustomerOrderNo((String) param.get("merOrderId"));
    sandBizRecord.setReqParams(JSONObject.toJSONString(param));
    sandBizRecord.insert();
    if (StringUtils.isBlank(result)) {
      throw new GlobalRunTimeException(ErrorCode.SYS_ERROR);
    }
    JSONObject jsonObject = JSONObject.parseObject(result);
    String resultCode = jsonObject.getString("resultCode");
    if (!"0000".equals(resultCode) && !"9999".equals(resultCode)) {
      log.error(
          "新生支付接口请求出错，错误码：{}，错误信息：{}",
          jsonObject.getString("errorCode"),
          jsonObject.getString("errorMsg"));
      throw new GlobalRunTimeException(jsonObject.getString("errorMsg"));
    }
    String verifyFieldStr = BaseTransForm.getVerifyDataByTranCode(tranCode, jsonObject);
    Sign sign = SecureUtil.sign(SignAlgorithm.SHA1withRSA, null, hnaConfig.getHnaPubKey());
    if (!sign.verify(
        verifyFieldStr.getBytes(), Base64.decode(jsonObject.getString("signValue").getBytes()))) {
      throw new GlobalRunTimeException(ErrorCode.SYS_ERROR);
    }
    return jsonObject;
  }

  private IdcardInfo checkPayOrder(MallOrder mallOrder, UserInfo user) {
    Assert.notNull(mallOrder, () -> new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND));
    Assert.isTrue(
        DateTime.now().isBefore(mallOrder.getExpiredTime()),
        () -> new GlobalRunTimeException(ErrorCode.ORDER_STATUS_ERROR));
    Assert.isTrue(
        mallOrder.getUserId().longValue() == user.getId().longValue(),
        () -> new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND));
    Assert.isTrue(
        mallOrder.getStatus() == MallOrderStatusEnum.WAIT_PAY,
        () -> new GlobalRunTimeException(ErrorCode.ORDER_STATUS_ERROR));
    IdcardInfo idcardInfo = idcardInfoDao.getByUserIdAndIsPass(user.getId());
    Assert.notNull(idcardInfo, () -> new GlobalRunTimeException(ErrorCode.USER_NOT_SM));
    Assert.isNull(
        mallPayOrderRecordService.getPaySuccessByOrderNo(mallOrder.getOrderNo()),
        () -> new GlobalRunTimeException(ErrorCode.ORDER_PAID));
    return idcardInfo;
  }

  private void checkNotifySign(JSONObject jsonObject, String verifyFieldStr) {
    log.info("新生支付快捷支付异步返回结果：{}", jsonObject.toJSONString());
    log.info("快捷支付异步通知准备验签数据：{}", verifyFieldStr);
    String signValue = URLDecoder.decode(jsonObject.getString("signValue"), StandardCharsets.UTF_8);
    Sign sign = SecureUtil.sign(SignAlgorithm.SHA1withRSA, null, hnaConfig.getHnaPubKey());
    if (!sign.verify(verifyFieldStr.getBytes(), Base64.decode(signValue.getBytes()))) {
      log.error("新生快捷支付异步回调接口返回参数验签不通过");
      throw new GlobalRunTimeException(ErrorCode.SYS_ERROR);
    }
  }

  private String needOpenAccount(long userId) {
    UserHnaExt userHnaExt = userHnaExtDao.getByUserId(userId);
    Assert.notNull(userHnaExt, () -> new GlobalRunTimeException("用户未开户"));
    Assert.isTrue(
        AccountStatusEnum.SUCCESS.equals(userHnaExt.getStatus()),
        () -> new GlobalRunTimeException("用户未开户"));
    Assert.isTrue(
        StringUtils.isNotBlank(userHnaExt.getUserNo()), () -> new GlobalRunTimeException("用户未开户"));
    return userHnaExt.getUserNo();
  }
}
